#include "pwext.hpp"
#include <stdio.h>
#include <unistd.h>

namespace pw {

// [[ex03ai]]
// DEBUG descr Condition variable implementation using an event, the second attempt.
class condvar
{
public:
	condvar() :
		ev_(false), // not signaled to staart with
		waiting_(0)
	{ }

	// m is locked on both entry and exit
	int wait(pw::pmutex *m)
	{
		++waiting_;
		{
			pw::unlockmutex ul(*m);
			ev_.wait();
		}
		if (--waiting_ == 0)
			ev_.reset();
		return 0;
	}
	// m is locked on both entry and exit
	int timedwait(pw::pmutex *m, 
		const struct timespec &abstime)
	{
		int res;
		++waiting_;
		{
			pw::unlockmutex ul(*m);
			res = ev_.timedwait(abstime);
		}
		if (--waiting_ == 0)
			ev_.reset();
		return res;
	}
	// the caller must have the same mutex as used in
	// wait() locked; unlike POSIX, this is a mandatory
	// requirement
	int broadcast()
	{
		if (waiting_ != 0)
			ev_.signal();
		return 0;
	}

protected:
	event ev_;
	// count of threads waiting for broadcast
	int waiting_; 
};
// END

};

// DEBUG {
pw::pmutex mtx;
pw::condvar cv;
int flag;

class waiter : public pw::pwthread
{
public:
	// auto-start on construction and stop before destruction
	waiter()
	{
		start();
	}
	~waiter()
	{
		join();
	}
	void *execute()
	{
		mtx.lock();
		while (flag == 0) {
			cv.wait(&mtx);
		}
		--flag;
		mtx.unlock();
		fprintf(stderr, "wait succeeded\n");
		return 0;
	}
};

int main()
{
	mtx.lock();
	cv.broadcast();

	struct timespec endtime;
	clock_gettime(CLOCK_REALTIME, &endtime); // the current time
	if (cv.timedwait(&mtx, endtime) == ETIMEDOUT)
		fprintf(stderr, "timedwait failed as expected\n");
	mtx.unlock();

	{
		waiter w1, w2, w3;
		usleep(100*1000);
		mtx.lock();
		flag += 3;
		cv.broadcast();
		mtx.unlock();
	}
	return 0;
}
// DEBUG }
/* Sample output:

timedwait failed as expected
wait succeeded
wait succeeded
wait succeeded

*/
